#include "game.h"
#include "game_intrinsicts.h"
#include "game_world.h"
#include "game_world.h"
#include "game_random.h"
#include "game_math.h"
#include "game_sim_region.h"
#include "game_audio.h"
#include "game_file_format.h"
#include "game_debug.h"
#include "game_cutscene.h"
#include "game_render_group.h"
#include "game_config.h"
internal void DebugOverlay(game_memory *Memory, game_input *Input);

[[maybe_unused]] internal game_state *GameStartup(void) {
    game_state *GameState = new game_state;
    if (GameState) {
#if 0  // 清理调试代码
        GameState->BlueOffset = 0;
        GameState->GreenOffset = 0;
        GameState->ToneHz = 256;
#endif
    }
    return GameState;
}

[[maybe_unused]] internal void GameShutDown(game_state *GameState) { delete GameState; }

internal void RenderPlayer(game_offscreen_buffer *Buffer, int PlayerX, int PlayerY) {
    // 定义矩形颜色，0xFFFFFFFF 表示白色 (RGBA: 全部通道为最大值)
    uint32 Color = 0xFFFFFFFF;

    // 定义矩形的四个边界
    int Left = PlayerX;         // 矩形的左边界（方块的 X 坐标）
    int Right = PlayerX + 10;   // 矩形的右边界（左边界 + 宽度 10 像素）
    int Top = PlayerY;          // 矩形的顶部边界（方块的 Y 坐标）
    int Bottom = PlayerY + 10;  // 矩形的底部边界（顶部边界 + 高度 10 像素）

    // 检查矩形是否超出屏幕边界，并对边界值进行修正
    if (Left < 0) {  // 如果左边界超出屏幕左边缘
        Left = 0;    // 将左边界设置为 0（屏幕最左侧）
    }
    if (Right > Buffer->Width) {  // 如果右边界超出屏幕右边缘
        Right = Buffer->Width;    // 将右边界设置为屏幕的宽度（最右侧）
    }
    if (Top < 0) {  // 如果顶部超出屏幕上边缘
        Top = 0;    // 将顶部设置为 0（屏幕最上侧）
    }
    if (Bottom > Buffer->Height) {  // 如果底部超出屏幕下边缘
        Bottom = Buffer->Height;    // 将底部设置为屏幕的高度（最下侧）
    }

    // 循环遍历矩形区域的每一行
    for (int Y = Top; Y < Bottom; ++Y) {
        // 计算当前行首个像素的位置
        // Buffer->Memory 是缓冲区起始地址，Y * Buffer->Pitch 表示移动到第 Y 行
        // Left * Buffer->BytesPerPixel 表示移动到矩形的起始列
        uint8 *Row = ((uint8 *)Buffer->Memory + Y * Buffer->Pitch + Left * BITMAP_BYTES_PER_PIEXLE);

        // 遍历当前行的每一列
        for (int X = Left; X < Right; ++X) {
            *(uint32 *)Row = Color;          // 将当前像素设置为白色（填充矩形）
            Row += BITMAP_BYTES_PER_PIEXLE;  // 移动到下一个像素（右移一个像素宽度）
        }
    }
}

bool32 ArenaHasRoomFor(memory_arena *Arena, memory_index SizeInit, arena_push_params Params) {
    memory_index Size = GetEffectiveSizeFor(Arena, SizeInit, Params);
    bool32 Result = ((Arena->Used + Size) <= Arena->Size);
    return Result;
}

char *PushString(memory_arena *Arena, const char *Source) {  //
    uint32 Size = 1;
    for (const char *At = Source; *At; At++) {
        ++Size;
    }
    char *Dest = (char *)PushSize_(Arena, Size, NoClear());
    for (uint32 CharIndex = 0; CharIndex < Size; ++CharIndex) {
        Dest[CharIndex] = Source[CharIndex];
    }
    return Dest;
}

char *PushAndNullTerminate(memory_arena *Arena, uint32 Length, const char *Source) {  //
    char *Dest = (char *)PushSize_(Arena, Length + 1, NoClear());
    for (uint32 CharIndex = 0; CharIndex < Length; ++CharIndex) {
        Dest[CharIndex] = Source[CharIndex];
    }
    Dest[Length] = 0;
    return Dest;
}

internal bool32 TestWall(real32 WallX, real32 RelX, real32 RelY, real32 PlayerDeltaX,
                         real32 PlayerDeltaY, real32 *tMin, real32 MinY, real32 MaxY) {
    bool32 Hit = false;
    // 定义一个很小的浮点值，用于处理浮点精度问题，避免计算误差导致逻辑错误。
    real32 tEpsilon = 0.01f;
    if (PlayerDeltaX != 0.0f) {
        // 计算当前瓦片的 X 方向上的碰撞时间 tResult
        // 玩家从当前相对位置 Rel.x 移动到右边墙 WallX 的碰撞时间
        real32 tResult = (WallX - RelX) / PlayerDeltaX;

        // 计算在 tResult 时刻玩家在 Y 方向上的位置
        // 使用线性插值公式，tResult 表示到达该墙的时间，PlayerDelta.y 表示玩家在 Y
        // 轴上的速度
        real32 Y = RelY + tResult * PlayerDeltaY;

        // 检查 tResult 是否大于等于 0，且 tMin 是否大于 tResult。
        // 如果 tResult 小于 0，表示碰撞发生在玩家起始点之前，不会考虑这个碰撞。
        // 如果 tMin 大于
        // tResult，表示当前碰撞距离比之前发现的碰撞距离更近，需更新最小碰撞时间。
        if ((tResult >= 0.0f) && (*tMin > tResult)) {
            // 检查计算得到的 Y
            // 坐标是否在瓦片的垂直范围内（即瓦片的最小角和最大角之间）。 只有当 Y
            // 值位于当前瓦片的有效范围内时，才能确认碰撞有效。
            if ((Y >= MinY) && (Y <= MaxY)) {
                // 更新 tMin 为当前 tResult，表示发现了一个更近的有效碰撞。
                // 由于浮点精度损失，角色走过顶部墙壁
                // 如果满足所有条件，更新最小碰撞时间 *tMin。
                // 减去一个小的 tEpsilon，用于处理浮点精度问题，确保玩家不会贴到墙上。
                *tMin = Maximum(0.0f, tResult - tEpsilon);  // 取最大值
                Hit = true;
            }
        }
    }
    return Hit;
}

task_with_memory *BeginTaskWithMemory(transient_state *TranState, bool32 DependsOnGameMode) {
    task_with_memory *FoundTask = 0;
    for (uint32 TaskIndex = 0; TaskIndex < ArrayCount(TranState->Tasks); TaskIndex++) {
        task_with_memory *Task = TranState->Tasks + TaskIndex;
        if (!Task->BeingUsed) {
            FoundTask = Task;
            Task->MemoryFlush = BeginTemporaryMemory(&Task->Arena);
            Task->BeingUsed = true;
            Task->DependsOnGameMode = DependsOnGameMode;
            break;
        }
    }
    return FoundTask;
}
void EndTaskWithMemory(task_with_memory *Task) {  //
    EndTemporaryMemory(Task->MemoryFlush);
    CompletePreviousWriteBeforeFutureWrites;
    Task->BeingUsed = false;
}

void EndRender(render_group *Group) {
    //
    (void)Group;
}

internal uint32 GetBestMatchAssetFrom(game_assets *Assets, assets_type_id TypeID,
                                      asset_vector *MatchVector, asset_vector *WeightVector) {
    TIMED_FUNCTION();
    (void)WeightVector;
    uint32 Result = {};

    real32 BestDiff = Real32Maximum;
    asset_type *Type = Assets->AssetTypes + TypeID;
    for (uint32 AssetIndex = Type->FirstAssetIndex; AssetIndex < Type->OnePastLastAssetIndex;
         ++AssetIndex) {
        asset *Asset = Assets->Assets + AssetIndex;
        real32 TotalWeightedDiff = 0.0f;
        for (uint32 TagIndex = Asset->HHA.FirstTagIndex; TagIndex < Asset->HHA.OnePastLastTagIndex;
             TagIndex++) {
            hha_tag *Tag = Assets->Tags + TagIndex;
            real32 A = MatchVector->E[Tag->ID];
            real32 B = Tag->Value;
            real32 D0 = AbsoluteValue(A - B);
            real32 D1 = AbsoluteValue(A - Assets->TagRange[Tag->ID] * SignOf(A) - B);
            real32 Difference = Minimum(D0, D1);

            real32 weighted = MatchVector->E[Tag->ID] * AbsoluteValue(Difference);
            TotalWeightedDiff += weighted;
        }
        if (BestDiff > TotalWeightedDiff) {
            BestDiff = TotalWeightedDiff;
            Result = AssetIndex;
        }
    }
    return Result;
}
// void LoadSound(game_assets *Assets, uint32 ID) {}
internal uint32 GetRandomSlotFrom(game_assets *Assets, assets_type_id TypeID,
                                  random_series *Series) {
    uint32 Result = {};
    asset_type *Type = Assets->AssetTypes + TypeID;
    if (Type->FirstAssetIndex != Type->OnePastLastAssetIndex) {
        uint32 Count = (Type->OnePastLastAssetIndex - Type->FirstAssetIndex);
        uint32 Choice = RandomChoice(Series, Count);
        Result = Type->FirstAssetIndex + Choice;
    }
    return Result;
}

bitmap_id GetRandomBitmapFrom(game_assets *Assets, assets_type_id TypeID, random_series *Series) {
    bitmap_id Result = {GetRandomSlotFrom(Assets, TypeID, Series)};
    return Result;
}

inline sound_id GetRandomSoundFrom(game_assets *Assets, assets_type_id TypeID,
                                   random_series *Series) {
    sound_id Result = {GetRandomSlotFrom(Assets, TypeID, Series)};
    return Result;
}

bitmap_id GetBestMatchBitmapFrom(game_assets *Assets, assets_type_id TypeID,
                                 asset_vector *MatchVector, asset_vector *WeightVector) {
    bitmap_id Result = {GetBestMatchAssetFrom(Assets, TypeID, MatchVector, WeightVector)};
    return Result;
}

inline bitmap_id GetBestMatchSoundFrom(game_assets *Assets, assets_type_id TypeID,
                                       asset_vector *MatchVector, asset_vector *WeightVector) {
    bitmap_id Result = {GetBestMatchAssetFrom(Assets, TypeID, MatchVector, WeightVector)};
    return Result;
}
font_id GetBestMatchFontFrom(game_assets *Asset, assets_type_id TypeID, asset_vector *MatchVector,
                             asset_vector *WeightVector) {
    font_id Result = {GetBestMatchAssetFrom(Asset, TypeID, MatchVector, WeightVector)};
    return Result;
}

internal void ClearBitmap(loaded_bitmap *Bitmap) {
    if (Bitmap->Memory) {
        // 计算整个位图所需的内存大小
        int32 TotalBitmapSize = Bitmap->Width * Bitmap->Height * BITMAP_BYTES_PER_PIEXLE;
        // 初始化分配的内存，将其清零
        ZeroSize(TotalBitmapSize, Bitmap->Memory);
    }
}

// 创建一个空白位图并返回
loaded_bitmap MakeEmptyBitmap(memory_arena *Arena, int32 Width, int32 Height,
                              bool32 ClearToZero = true) {
    // 初始化返回的位图结构
    loaded_bitmap Result = {};
    Result.AlignPrecentage = {0.5f, 0.5f};
    Result.WidthOverHeight = SafeRatio1((real32)Width, (real32)Height);
    // 设置位图的宽度和高度
    Result.Width = SafeTruncateUInt16(Width);
    Result.Height = SafeTruncateUInt16(Height);

    // 计算位图的步幅，步幅是每行像素所占的字节数
    // 每个像素占用的字节数乘以宽度
    Result.Pitch = Result.Width * BITMAP_BYTES_PER_PIEXLE;

    // 计算整个位图所需的内存大小
    int32 TotalBitmapSize = Width * Height * BITMAP_BYTES_PER_PIEXLE;

    // 从内存池中分配所需的内存
    Result.Memory = PushSize(Arena, TotalBitmapSize, Align(16, ClearToZero));
    if (ClearToZero) {
        ClearBitmap(&Result);
    }
    // 返回创建的空白位图
    return Result;
}

// 创建球形法线贴图
internal void MakeSphereNormalMap(loaded_bitmap *BitMap, real32 Roughness, real32 Cx = 1.0f,
                                  real32 Cy = 1.0f) {  //
    // 计算宽度和高度的倒数，用于归一化坐标
    real32 InvWidth = 1.0f / (real32)(BitMap->Width - 1.0f);
    real32 InvHeight = 1.0f / (real32)(BitMap->Height - 1.0f);
    uint8 *Row = (uint8 *)BitMap->Memory;
    // 获取图像内存的指针

    // 遍历每一行（Y轴）
    for (int32 Y = 0; Y < BitMap->Height; Y++) {
        uint32 *Pixel = (uint32 *)Row;
        // 获取当前行的像素数据
        // 遍历每一列（X轴）
        for (int32 X = 0; X < BitMap->Width; X++) {
            // 根据X和Y坐标计算对应的UV值（归一化坐标）
            v2 BitmapUV = {InvWidth * (real32)X, InvHeight * (real32)Y};

            // 将BitmapUV的x和y坐标映射到-1到1的范围
            real32 Nx = Cx * (2.0f * BitmapUV.x - 1.0f);  // 将x坐标从[0, 1]映射到[-1, 1]
            real32 Ny = Cy * (2.0f * BitmapUV.y - 1.0f);  // 将y坐标从[0, 1]映射到[-1, 1]

            // 计算 RootTerm，它代表的是与当前向量 Nx 和 Ny 的分量相关的平方根项。
            // RootTerm 计算的是 z 分量的平方，即 1 - Nx^2 - Ny^2
            real32 RootTerm = 1.0f - Nx * Nx - Ny * Ny;

            // 0.7071067811865475 是 1/sqrt(2) 的值，这个值用于构建法线向量的 Y 和 Z 分量。
            v3 Normal = {0, 0.7071067811865475f, 0.7071067811865475f};

            // 计算法线的 Nz 分量，默认设置为 0.0f。
            // 这个 Nz 是 RootTerm 的计算结果，通过它可以得出完整的法线向量。
            real32 Nz = 0.0f;
            if (RootTerm >= 0.0f) {
                // 使用Pythagorean定理计算z坐标，确保法线向量的单位长度为1
                Nz = SquareRoot(RootTerm);  // 计算z坐标，使法线向量的长度为1
                // 创建法线向量
                Normal = {Nx, Ny, Nz};  // 将计算得到的Nx, Ny, Nz组成法线向量
            }

            // 使用法线值生成颜色，归一化后的法线值用于RGB通道，粗糙度用于A通道
            v4 Color = {255.0f * (0.5f * (Normal.x + 1.0f)),
                        // 红色通道（映射到[0, 255]范围）
                        255.0f * (0.5f * (Normal.y + 1.0f)),
                        // 绿色通道（映射到[0, 255]范围）
                        255.0f * (0.5f * (Normal.z + 1.0f)),
                        // 蓝色通道（映射到[0, 255]范围）
                        255.0f * Roughness};
            // 粗糙度通道（映射到[0, 255]范围）

            // 将计算出的颜色值存入当前像素
            *Pixel++ = (((uint32)(Color.a + 0.5f) << 24)  // 将A通道值放到目标像素的Alpha位置
                        | ((uint32)(Color.r + 0.5f) << 16)  // 将红色通道值放到目标像素的红色位置
                        | ((uint32)(Color.g + 0.5f) << 8)  // 将绿色通道值放到目标像素的绿色位置
                        | ((uint32)(Color.b + 0.5f) << 0));  // 将蓝色通道值放到目标像素的蓝色位置;
        }
        Row += BitMap->Pitch;
        // 移动到下一行
    }
}
internal void MakeSphereDiffuseMap(loaded_bitmap *BitMap, real32 Cx = 1.0f,
                                   real32 Cy = 1.0f) {  //
    // 计算宽度和高度的倒数，用于归一化坐标
    real32 InvWidth = 1.0f / (real32)(BitMap->Width - 1.0f);    // 宽度倒数
    real32 InvHeight = 1.0f / (real32)(BitMap->Height - 1.0f);  // 高度倒数

    uint8 *Row = (uint8 *)BitMap->Memory;  // 获取图像内存的指针

    // 遍历每一行（Y轴）
    for (int32 Y = 0; Y < BitMap->Height; Y++) {
        uint32 *Pixel = (uint32 *)Row;  // 获取当前行的像素数据
        // 遍历每一列（X轴）
        for (int32 X = 0; X < BitMap->Width; X++) {
            // 根据X和Y坐标计算对应的UV值（归一化坐标）
            v2 BitmapUV = {InvWidth * (real32)X, InvHeight * (real32)Y};

            // 将BitmapUV的x和y坐标映射到-1到1的范围
            real32 Nx = Cx * (2.0f * BitmapUV.x - 1.0f);  // 将x坐标从[0, 1]映射到[-1, 1]
            real32 Ny = Cy * (2.0f * BitmapUV.y - 1.0f);  // 将y坐标从[0, 1]映射到[-1, 1]

            // 计算 RootTerm，它代表的是与当前向量 Nx 和 Ny 的分量相关的平方根项。
            // RootTerm 计算的是 z 分量的平方，即 1 - Nx^2 - Ny^2
            real32 RootTerm = 1.0f - Nx * Nx - Ny * Ny;

            // 判断 RootTerm 是否大于等于零，如果大于等于零，则Alpha值为1.0，表示该点可见
            real32 Alpha = 0.0f;
            if (RootTerm >= 0.0f) {
                Alpha = 1.0f;  // 如果RootTerm为正，则Alpha设为1，表示该点是有效的
            }

            v3 BaseColor = {0.0f, 0.0f, 0.0f};
            Alpha *= 255.0f;
            // 根据计算出的Alpha值，生成最终的颜色值（带有透明度）
            v4 Color = {Alpha * BaseColor.x,  //
                        Alpha * BaseColor.y,  //
                        Alpha * BaseColor.z,  //
                        Alpha};  // 将RGB值调整为[0, 255]范围，并计算透明度（Alpha）

            // 将计算出的颜色值存入当前像素
            *Pixel++ = (((uint32)(Color.a + 0.5f) << 24)  // 将A通道值放到目标像素的Alpha位置
                        | ((uint32)(Color.r + 0.5f) << 16)  // 将红色通道值放到目标像素的红色位置
                        | ((uint32)(Color.g + 0.5f) << 8)  // 将绿色通道值放到目标像素的绿色位置
                        | ((uint32)(Color.b + 0.5f) << 0));  // 将蓝色通道值放到目标像素的蓝色位置
        }
        Row += BitMap->Pitch;  // 移动到下一行
    }
}

// 创建金字塔法线贴图
internal void MakePyramidNormalMap(loaded_bitmap *BitMap, real32 Roughness) {  //
    // 计算宽度和高度的倒数，用于归一化坐标
    real32 InvWidth = 1.0f / (real32)(BitMap->Width - 1.0f);
    real32 InvHeight = 1.0f / (real32)(BitMap->Height - 1.0f);
    uint8 *Row = (uint8 *)BitMap->Memory;
    // 获取图像内存的指针

    // 遍历每一行（Y轴）
    for (int32 Y = 0; Y < BitMap->Height; Y++) {
        uint32 *Pixel = (uint32 *)Row;
        // 获取当前行的像素数据
        // 遍历每一列（X轴）
        for (int32 X = 0; X < BitMap->Width; X++) {
            // 根据X和Y坐标计算对应的UV值（归一化坐标）
            v2 BitmapUV = {InvWidth * (real32)X, InvHeight * (real32)Y};
            (void)BitmapUV;

            int32 InvX = (BitMap->Width - 1) - X;
            real32 Seven = 0.7071067811865475f;
            v3 Normal = {0, 0, Seven};

            if (X < Y) {
                if (InvX < Y) {
                    Normal.x = -Seven;
                } else {
                    Normal.y = Seven;
                }
            } else {
                if (InvX < Y) {
                    Normal.y = -Seven;
                } else {
                    Normal.x = Seven;
                }
            }

            // 使用法线值生成颜色，归一化后的法线值用于RGB通道，粗糙度用于A通道
            v4 Color = {255.0f * (0.5f * (Normal.x + 1.0f)),
                        // 红色通道（映射到[0, 255]范围）
                        255.0f * (0.5f * (Normal.y + 1.0f)),
                        // 绿色通道（映射到[0, 255]范围）
                        255.0f * (0.5f * (Normal.z + 1.0f)),
                        // 蓝色通道（映射到[0, 255]范围）
                        255.0f * Roughness};
            // 粗糙度通道（映射到[0, 255]范围）

            // 将计算出的颜色值存入当前像素
            *Pixel++ = (((uint32)(Color.a + 0.5f) << 24)  // 将A通道值放到目标像素的Alpha位置
                        | ((uint32)(Color.r + 0.5f) << 16)  // 将红色通道值放到目标像素的红色位置
                        | ((uint32)(Color.g + 0.5f) << 8)  // 将绿色通道值放到目标像素的绿色位置
                        | ((uint32)(Color.b + 0.5f) << 0));  // 将蓝色通道值放到目标像素的蓝色位置;
        }
        Row += BitMap->Pitch;
        // 移动到下一行
    }
}

#if 0
// 函数：RequestGroundBuffers
// 说明：根据给定的中心位置和边界，计算偏移后的区域，并请求填充地面缓冲区。
// 参数：
//   - CenterP：世界坐标中的中心点，用于计算偏移。
//   - Bounds：要填充的区域的边界。
// 返回值：
//   - 无，函数直接执行填充操作。
internal void RequestGroundBuffers(world_position CenterP, rectangle3 Bounds) {
    // 通过偏移计算调整后的边界，将中心点的偏移量加到原边界上
    Bounds = Offset(Bounds, CenterP.Offset_);

    // 设置中心位置的偏移量为零向量
    // 这意味着 CenterP 不再有任何偏移，位置将直接对齐到其原始坐标
    CenterP.Offset_ = {0, 0, 0};

    // 调用填充地面块的函数，根据当前状态、游戏状态和偏移后的边界填充地面缓冲区
    FillGroundChunk(TranState, GameState, TranState->GroundBuffers, &GameState->CameraP);
}
#endif

#if GAME_INTERNAL
game_memory *DebugGlobalMemory;
#endif
#if 0
internal void BeginAssetType(game_assets *Assets, assets_type_id TypeID) {
    Assert(Assets->DEBUGAssetType == 0);
    Assets->DEBUGAssetType = Assets->AssetTypes + TypeID;
    Assets->DEBUGAssetType->TypeID = TypeID;
    Assets->DEBUGAssetType->FirstAssetIndex = Assets->DEBUGUsedAssetCount;
    Assets->DEBUGAssetType->OnePastLastAssetIndex = Assets->DEBUGAssetType->FirstAssetIndex;
}

internal void AddTag(game_assets *Assets, asset_tag_id ID, real32 Value) {
    Assert(Assets->DEBUGAsset);
    ++Assets->DEBUGAsset->OnePastLastTagIndex;
    asset_tag *Tag = Assets->Tags + Assets->DEBUGUsedTagCount++;
    Tag->ID = ID;
    Tag->Value = Value;
}

internal bitmap_id AddBitmapAsset(game_assets *Assets, const char *FileName,
                                  v2 AlignPercentage = {0.5f, 0.5f}) {
    Assert(Assets->DEBUGAssetType);
    Assert(Assets->DEBUGAssetType->OnePastLastAssetIndex < Assets->AssetCount);
    bitmap_id Result = {Assets->DEBUGAssetType->OnePastLastAssetIndex++};
    asset *Asset = Assets->Assets + Result.Value;
    Asset->FirstTagIndex = Assets->DEBUGUsedTagCount;
    Asset->FirstTagIndex = Asset->FirstTagIndex;
    Asset->OnePastLastTagIndex = Asset->FirstTagIndex;
    Asset->Bitmap.AlignPercentage = AlignPercentage;
    Asset->Bitmap.FileName = PushString(&Assets->AssetArena, FileName);

    Assets->DEBUGAsset = Asset;
    return Result;
}

internal sound_id AddSoundAsset(game_assets *Assets, const char *FileName,
                                uint32 FirstSampleIndex = 0, uint32 SampleCount = 0) {
    Assert(Assets->DEBUGAssetType);
    Assert(Assets->DEBUGAssetType->OnePastLastAssetIndex < Assets->AssetCount);

    sound_id Result = {Assets->DEBUGAssetType->OnePastLastAssetIndex++};
    asset *Asset = Assets->Assets + Result.Value;
    Asset->FirstTagIndex = Assets->DEBUGUsedTagCount;
    Asset->FirstTagIndex = Asset->FirstTagIndex;
    Asset->OnePastLastTagIndex = Asset->FirstTagIndex;
    Asset->Sound.FileName = PushString(&Assets->AssetArena, FileName);
    Asset->Sound.NextIDToPlay = {0};
    Asset->Sound.FirstSampleIndex = FirstSampleIndex;
    Asset->Sound.SampleCount = SampleCount;
    Assets->DEBUGAsset = Asset;
    return Result;
}

internal void EndAssetType(game_assets *Assets) {
    Assert(Assets->DEBUGAssetType);
    Assets->DEBUGUsedAssetCount = Assets->DEBUGAssetType->OnePastLastAssetIndex;
    Assets->DEBUGAssetType = 0;
    Assets->DEBUGAsset = 0;
}
#endif

asset_memory_block *InsertBlock(asset_memory_block *Prev, memory_index Size, void *Memory) {
    Assert(Size > sizeof(asset_memory_block));
    asset_memory_block *Block = (asset_memory_block *)Memory;
    Block->Flags = 0;
    Block->Size = Size - sizeof(asset_memory_block);
    Block->Prev = Prev;
    Block->Next = Prev->Next;
    Block->Prev->Next = Block;
    Block->Next->Prev = Block;
    return Block;
}

internal game_assets *AllocateGameAssets(memory_arena *Arena, memory_index Size,
                                         transient_state *TranState,
                                         platform_texture_op_queue *TextureOpQueue) {
    TIMED_FUNCTION();
    game_assets *Assets = PushStruct(Arena, game_assets);
    Assets->TextureOpQueue = TextureOpQueue;
    Assets->NextGanerationID = 0;
    Assets->InFlightGenerationCount = 0;

    Assets->MemorySentinel.Flags = 0;
    Assets->MemorySentinel.Size = 0;
    Assets->MemorySentinel.Prev = &Assets->MemorySentinel;
    Assets->MemorySentinel.Next = &Assets->MemorySentinel;

    InsertBlock(&Assets->MemorySentinel, Size, PushSize(Arena, Size, NoClear()));

    Assets->TranState = TranState;

    Assets->LoadedAssetSentinel.Next = Assets->LoadedAssetSentinel.Prev =
        &Assets->LoadedAssetSentinel;

    for (uint32 TagType = 0; TagType < Tag_Count; TagType++) {
        Assets->TagRange[TagType] = 1000000.0f;
    }
    Assets->TagRange[Tag_FacingDirection] = Tau32;

#if 1

    Assets->TagCount = 1;
    Assets->Tags = 0;
    Assets->AssetCount = 2 * 256 * Asset_Count;
    Assets->Assets = PushArray(Arena, Assets->AssetCount, asset);

    Assets->TagCount = 1024 * Asset_Count;
    Assets->Tags = PushArray(Arena, Assets->TagCount, hha_tag);
    {
        Assets->TagCount = 0;
        Assets->AssetCount = 1;
        platform_file_group FileGroup = Platform.GetAllFilesOfTypeBegin(PlatformFileType_AssetFile);

        Assets->FileCount = FileGroup.FileCount;
        Assets->Files = PushArray(Arena, Assets->FileCount, asset_file);
        for (uint32 FileIndex = 0; FileIndex < Assets->FileCount; ++FileIndex) {
            asset_file *File = Assets->Files + FileIndex;
            File->FontBitmapIDOffset = 0;
            File->TagBase = Assets->TagCount;
            ZeroStruct(File->Header);
            File->Handle = Platform.OpenNextFile(&FileGroup);
            Platform.ReadDataFromFile(&File->Handle, 0, sizeof(File->Header), &File->Header);
            uint32 AssetTypeArraySize = File->Header.AssetTypeCount * sizeof(hha_asset_type);
            File->AssetTypeArray = (hha_asset_type *)PushSize(Arena, AssetTypeArraySize);
            Platform.ReadDataFromFile(&File->Handle,            //
                                      File->Header.AssetTypes,  //
                                      AssetTypeArraySize,       //
                                      File->AssetTypeArray);
            hha_header *Header = &File->Header;
            if (Header->MagicValue != HHA_MAGIC_VALUE) {
                Platform.FileError(&File->Handle, "hha file has an valid magic value");
            }
            if (Header->Version > HHA_VERSION) {
                Platform.FileError(&File->Handle, "hha file is of a later version");
            }
            if (PlatformNoFileErrors(&File->Handle)) {
                Assets->TagCount += (Header->TagCount - 1);
                // 注意：每个 HHA 文件的第一个槽位是一个预留的空资产（null asset）。
                Assets->AssetCount += Header->AssetCount - 1;
            } else {
                InvalidCodePath
            }
        }
        Platform.GetAllFilesOfTypeEnd(&FileGroup);
    }
    Assets->Assets->HHA = *PushArray(Arena, Assets->AssetCount, hha_asset);
    Assets->Assets = PushArray(Arena, Assets->AssetCount, asset);
    Assets->Tags = PushArray(Arena, Assets->TagCount, hha_tag);

    ZeroStruct(Assets->Tags[0]);

    for (uint32 FileIndex = 0; FileIndex < Assets->FileCount; ++FileIndex) {
        asset_file *File = Assets->Files + FileIndex;
        if (PlatformNoFileErrors(&File->Handle)) {
            uint32 TagArraySize = sizeof(hha_tag) * (File->Header.TagCount - 1);
            Platform.ReadDataFromFile(&File->Handle, File->Header.Tags + sizeof(hha_tag),
                                      TagArraySize, Assets->Tags + File->TagBase);
        }
    }
    uint32 AssetCount = 0;
    ZeroStruct(*(Assets->Assets + AssetCount));
    ++AssetCount;
    for (uint32 DestTypeID = 0; DestTypeID < Asset_Count; ++DestTypeID) {
        asset_type *DestType = Assets->AssetTypes + DestTypeID;
        DestType->FirstAssetIndex = AssetCount;

        for (uint32 FileIndex = 0; FileIndex < Assets->FileCount; ++FileIndex) {
            asset_file *File = Assets->Files + FileIndex;
            if (PlatformNoFileErrors(&File->Handle)) {
                for (uint32 SourceIndex = 0; SourceIndex < File->Header.AssetTypeCount;
                     ++SourceIndex) {
                    hha_asset_type *SourceType = File->AssetTypeArray + SourceIndex;
                    if (SourceType->TypeID == DestTypeID) {
                        if (SourceType->TypeID == Asset_FontGlyph) {
                            File->FontBitmapIDOffset = AssetCount - SourceType->FirstAssetIndex;
                        }
                        uint32 AssetCountForType = (SourceType->OnePastLastAssetIndex -  //
                                                    SourceType->FirstAssetIndex);
                        temporary_memory TempMem = BeginTemporaryMemory(&TranState->TransientArena);
                        hha_asset *HHAAssetArray =
                            PushArray(&TranState->TransientArena, AssetCountForType, hha_asset);

                        Platform.ReadDataFromFile(
                            &File->Handle,                                        //
                            File->Header.Assets +                                 //
                                SourceType->FirstAssetIndex * sizeof(hha_asset),  //
                            AssetCountForType * sizeof(hha_asset),                //
                            HHAAssetArray);

                        for (uint32 AssetIndex = 0; AssetIndex < AssetCountForType; ++AssetIndex) {
                            hha_asset *HHAAsset = HHAAssetArray + AssetIndex;
                            Assert(AssetCount < Assets->AssetCount);
                            asset *Asset = Assets->Assets + AssetCount++;
                            Asset->HHA = *HHAAsset;
                            Asset->FileIndex = FileIndex;
                            if (Asset->HHA.FirstTagIndex == 0) {
                                Asset->HHA.FirstTagIndex = Asset->HHA.OnePastLastTagIndex = 0;
                            } else {
                                Asset->HHA.FirstTagIndex += (File->TagBase - 1);
                                Asset->HHA.OnePastLastTagIndex += (File->TagBase - 1);
                            }
                        }
                        EndTemporaryMemory(TempMem);
                    }
                }
            }
        }
        DestType->OnePastLastAssetIndex = AssetCount;
    }
    Assert(AssetCount == Assets->AssetCount);
#endif
#if 0
    debug_read_file_result ReadResult = Platform.DEBUGPlatformReadEntireFile("test.hha");
    if (ReadResult.ContentsSize != 0) {
        hha_header *Header = (hha_header *)ReadResult.Contents;

        Assert(Header->MagicValue == HHA_MAGIC_VALUE);
        Assert(Header->Version == HHA_VERSION);

        Assets->AssetCount = Header->AssetCount;
        Assets->Assets = PushArray(Arena, Assets->AssetCount, asset);
        Assets->Assets->HHA = *(hha_asset *)((uint8 *)ReadResult.Contents + Header->Assets);
        Assets->Slots = PushArray(Arena, Assets->AssetCount, asset_slot);

        Assets->TagCount = Header->TagCount;
        Assets->Tags = (hha_tag *)((uint8 *)ReadResult.Contents + Header->Tags);

        hha_asset_type *HHAAssetTypes =
            (hha_asset_type *)((uint8 *)ReadResult.Contents + Header->AssetTypes);

        for (uint32 Index = 0; Index < Header->AssetTypeCount; Index++) {
            hha_asset_type *Source = HHAAssetTypes + Index;
            if (Source->TypeID < Asset_Count) {
                asset_type *Dest = Assets->AssetTypes + Source->TypeID;
                Assert(Dest->FirstAssetIndex == 0);
                Assert(Dest->OnePastLastAssetIndex == 0);
                Dest->FirstAssetIndex = Source->FirstAssetIndex;
                Dest->OnePastLastAssetIndex = Source->OnePastLastAssetIndex;
            }
        }
        Assets->HHAContents = (uint8 *)ReadResult.Contents;
    }
#endif
#if 0
    Assets->DEBUGUsedAssetCount = 1;

    BeginAssetType(Assets, Asset_Shadow);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_shadow.bmp",
                   {0.500000000f, 0.156682029f});
    EndAssetType(Assets);
    BeginAssetType(Assets, Asset_Tree);

    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tree00.bmp",
                   {0.493827164f, 0.295652181f});
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Rock);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/rock03.bmp",
                   {0.344827592f, 0.656250000f});
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Grass);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/grass00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/grass01.bmp");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Tuft);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft01.bmp");
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/tuft02.bmp");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Stone);
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground00.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground01.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground02.bmp");
    // 调用 `DebugLoadBMP` 函数加载位图数据
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test2/ground03.bmp");
    EndAssetType(Assets);

    real32 AngleRight = 0.0f * Tau32;
    real32 AngleBack = 0.25f * Tau32;
    real32 AngleLeft = 0.5f * Tau32;
    real32 AngleFront = 0.75f * Tau32;

    v2 HeroAlign = {0.5f, 0.156682029f};
    BeginAssetType(Assets, Asset_Head);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_head.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_head.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_head.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_head.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Cape);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_cape.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_cape.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_cape.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_cape.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Torso);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_right_torso.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleRight);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_back_torso.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleBack);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_left_torso.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleLeft);
    AddBitmapAsset(Assets, "../../../../game_test_asset/data/test/test_hero_front_torso.bmp",
                   HeroAlign);
    AddTag(Assets, Tag_FacingDirection, AngleFront);
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Bloop);
    sound_id Bloop = AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_00.wav");
    Assets->Assets[Bloop.Value].Sound.NextIDToPlay = Bloop;
#if 0
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_01.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_02.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_03.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/bloop_04.wav");
#endif
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Crack);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/crack_00.wav");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Drop);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Drop_00.wav");
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Glide);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Glide_00.wav");
    EndAssetType(Assets);

    uint32 OneMusicChunk = 10 * 48000;
    uint32 TotalMusicSampleCount = 7468095;
    BeginAssetType(Assets, Asset_Music);
    sound_id LastMusic = {0};
    for (uint32 FirstSampleIndex = 0; FirstSampleIndex < TotalMusicSampleCount;
         FirstSampleIndex += OneMusicChunk) {
        uint32 SampleCount = TotalMusicSampleCount - FirstSampleIndex;
        if (SampleCount > OneMusicChunk) {
            SampleCount = OneMusicChunk;
        }
        sound_id ThisMusic =
            AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Music_test.wav",
                          FirstSampleIndex, SampleCount);
        if (IsValid(LastMusic)) {
            Assets->Assets[LastMusic.Value].Sound.NextIDToPlay = ThisMusic;
        }
        LastMusic = ThisMusic;
    }
    EndAssetType(Assets);

    BeginAssetType(Assets, Asset_Puhp);
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Puhp_00.wav");
    AddSoundAsset(Assets, "../../../../game_test_asset/data/test3/Puhp_01.wav");
    EndAssetType(Assets);

#if 0
    hero_bitmaps *Bitmap;
    Bitmap = Assets->HeroBitmaps;
    // 加载玩家角色右侧的头部图像
    Bitmap->Head = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_right_head.bmp");
    // 加载玩家角色右侧的斗篷图像
    Bitmap->Cape = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_right_cape.bmp");
    // 加载玩家角色右侧的躯干图像
    Bitmap->Torso = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_right_torso.bmp");
    SetTopDownAlign(Bitmap, v2{72, 182});
    ++Bitmap;

    // 加载玩家角色背部的头部图像
    Bitmap->Head = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_back_head.bmp");
    // 加载玩家角色背部的斗篷图像
    Bitmap->Cape = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_back_cape.bmp");
    // 加载玩家角色背部的躯干图像
    Bitmap->Torso = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_back_torso.bmp");
    SetTopDownAlign(Bitmap, v2{72, 182});
    ++Bitmap;

    // 加载玩家角色左侧的头部图像
    Bitmap->Head = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_left_head.bmp");
    // 加载玩家角色左侧的斗篷图像
    Bitmap->Cape = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_left_cape.bmp");
    // 加载玩家角色左侧的躯干图像
    Bitmap->Torso = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_left_torso.bmp");

    SetTopDownAlign(Bitmap, v2{72, 182});
    ++Bitmap;

    // 加载玩家角色正面的头部图像
    Bitmap->Head = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_front_head.bmp");
    // 加载玩家角色正面的斗篷图像
    Bitmap->Cape = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_front_cape.bmp");
    // 加载玩家角色正面的躯干图像
    Bitmap->Torso = DebugLoadBMP("../../../../game_test_asset/data/test/test_hero_front_torso.bmp");

    SetTopDownAlign(Bitmap, v2{72, 182});
    ++Bitmap;

#endif
#endif
    return (Assets);
}
#if GAME_INTERNAL
#define STB_TRUETYPE_IMPLEMENTATION 1
#include "stb_truetype.h"
loaded_bitmap MakeNothingsTest(memory_arena *Arena) {
    debug_read_file_result TTFFile =
        Platform.DEBUGPlatformReadEntireFile("C:/Windows/Fonts/arial.ttf");
    stbtt_fontinfo Font;
    stbtt_InitFont(&Font, (const unsigned char *)TTFFile.Contents,  //
                   stbtt_GetFontOffsetForIndex((const unsigned char *)TTFFile.Contents, 0));
    int Width, Height, XOffset, YOffset;
    uint8 *MonoBitmap = stbtt_GetCodepointBitmap(&Font, 0,                                  //
                                                 stbtt_ScaleForPixelHeight(&Font, 128.0f),  //
                                                 'N', &Width, &Height, &XOffset, &YOffset);
    loaded_bitmap Result = MakeEmptyBitmap(Arena, Width, Height, false);
    uint8 *Source = MonoBitmap;
    uint8 *DestRow = (uint8 *)Result.Memory + (Height - 1) * Result.Pitch;
    for (int Y = 0; Y < Height; Y++) {
        uint32 *Dest = (uint32 *)DestRow;
        for (int X = 0; X < Width; X++) {
            uint8 Alpha = *Source++;
            *Dest++ = ((Alpha << 24) |  //
                       (Alpha << 16) |  //
                       (Alpha << 8) |   //
                       (Alpha << 0));
        }
        DestRow -= Result.Pitch;
    }
    stbtt_FreeBitmap(MonoBitmap, 0);
    return Result;
}
#endif  // GAME_INTERNAL

uint32 DEBUGGetMainGenerationID(game_memory *Memory) {
    uint32 Result = 0;
    transient_state *TranState = (transient_state *)Memory->TransientStorage;
    if (TranState->Initialized) {
        Result = TranState->MainGenerationID;
    }
    return Result;
}

game_assets *DebugGetGameAssets(game_memory *Memory) {
    game_assets *Assets = 0;
    transient_state *TranState = (transient_state *)Memory->TransientStorage;
    if (TranState->Initialized) {
        Assets = TranState->Assets;
    }
    return Assets;
}

void SetGameMode(game_state *GameState, transient_state *TranState, game_mode GameMode) {
    bool32 NeedToWait = false;
    for (uint32 TaskIndex = 0; TaskIndex < ArrayCount(TranState->Tasks); ++TaskIndex) {
        NeedToWait = NeedToWait || TranState->Tasks[TaskIndex].DependsOnGameMode;
    }
    if (NeedToWait) {
        Platform.CompleteAllWork(TranState->LowPriorityQueue);
    }
    Clear(&GameState->ModeArena);
    GameState->GameMode = GameMode;
}
GAME_UPDATE_AND_RENDER(GameUpdateAndRender) {
    // 游戏更新和渲染函数
    GlobalDebugTable = Memory->DebugTable;
    const char *FileName = __FILE__;
    const char *FunctionName = __FUNCTION__;
    int LineNumber = __LINE__;
    (void)FileName;
    (void)FunctionName;
    (void)LineNumber;
#if GAME_INTERNAL
    {
        DEBUG_DATA_BLOCK("DEBUG");
        DEBUG_B32(Global_Debug_FullGUIDName);
    }
    {
        DEBUG_DATA_BLOCK("Renderer");
        DEBUG_B32(Global_Renderer_TestWeirdRrawBufferSize);
        DEBUG_B32(Global_Renderer_ShowLightingSamples);
        {
            DEBUG_DATA_BLOCK("Renderer/Camera");
            DEBUG_B32(Global_Renderer_Camera_UsedDebug);
            DEBUG_VALUE(Global_Renderer_Camera_DebugDistance);
            DEBUG_B32(Global_Renderer_Camera_UseRoomBase);
        }
    }
    {
        DEBUG_DATA_BLOCK("GroundChunks");
        DEBUG_B32(Global_GroundChunks_RecomputeEXEChange);
        DEBUG_B32(Global_GroundChunks_Checkerboards);
        DEBUG_B32(Global_GroundChunks_Outlines);
        DEBUG_B32(Global_GroundChunksOn);
    }

    {
        DEBUG_DATA_BLOCK("Particle");
        DEBUG_B32(Global_Particle_ParticleTest);
        DEBUG_B32(Global_Particle_ParticleGrid);
    }
    {
        DEBUG_DATA_BLOCK("AI");
        {
            DEBUG_DATA_BLOCK("AI/Familiar");
            DEBUG_B32(Global_AI_Familiar_FollowsHero);
        }
    }
    {
        DEBUG_DATA_BLOCK("Entities");
        DEBUG_B32(Global_Entities_UseSpacesOutlines);
        DEBUG_B32(Global_Entities_DrawEntityOutline);
    }
    {
        DEBUG_DATA_BLOCK("Profile");
        DEBUG_UI_ELEMENT(DebugType_FrameSlider, FrameSlider)
        DEBUG_UI_ELEMENT(DebugType_LastFrameInfo, GameUpdateAndRender);
        DEBUG_UI_ELEMENT(DebugType_DebugMemoryInfo, GameUpdateAndRender);
        DEBUG_UI_ELEMENT(DebugType_TopClocksList, GameUpdateAndRender)
    }
    {
        DEBUG_DATA_BLOCK("Simulation");
        DEBUG_VALUE(Global_Timestep_Percentage);
    }
    Input->dtForFrame *= Global_Timestep_Percentage / 100.0f;
    DebugGlobalMemory = Memory;
    Platform.DEBUGPlatformReadEntireFile = Memory->PlatformAPI.DEBUGPlatformReadEntireFile;
#endif
    Assert((&(Input->Controllers[0].Terminator) - &(Input->Controllers[0].Buttons[0])) ==
           (ArrayCount(Input->Controllers[0].Buttons)));

    Assert(sizeof(game_state) <= Memory->PermanentStorageSize);
    game_state *GameState = (game_state *)Memory->PermanentStorage;

    Platform.AddEntry = Memory->PlatformAPI.AddEntry;
    Platform.CompleteAllWork = Memory->PlatformAPI.CompleteAllWork;

    Platform.GetAllFilesOfTypeBegin = Memory->PlatformAPI.GetAllFilesOfTypeBegin;
    Platform.GetAllFilesOfTypeEnd = Memory->PlatformAPI.GetAllFilesOfTypeEnd;
    Platform.OpenNextFile = Memory->PlatformAPI.OpenNextFile;
    Platform.ReadDataFromFile = Memory->PlatformAPI.ReadDataFromFile;
    Platform.FileError = Memory->PlatformAPI.FileError;

#if GAME_INTERNAL
    // 指向临时存储的指针，用于存储短期的数据，如帧缓冲、临时计算等。
    Platform.DEBUGPlatformReadEntireFile = Memory->PlatformAPI.DEBUGPlatformReadEntireFile;
    Platform.DEBUGPlatformFreeFileMemory = Memory->PlatformAPI.DEBUGPlatformFreeFileMemory;
    Platform.DEBUGPlatformWriteEntireFile = Memory->PlatformAPI.DEBUGPlatformWriteEntireFile;
    Platform.DEBUGPlatformExecuteSystemCommand =
        Memory->PlatformAPI.DEBUGPlatformExecuteSystemCommand;
    Platform.DEBUGPlatformCloseFileHandle = Memory->PlatformAPI.DEBUGPlatformCloseFileHandle;
    Platform.DEBUGPlatformGetProcessState = Memory->PlatformAPI.DEBUGPlatformGetProcessState;
#endif  // GAME_INTERNAL

    Platform.AllocateMemory = Memory->PlatformAPI.AllocateMemory;
    Platform.DeallocateMemory = Memory->PlatformAPI.DeallocateMemory;

    // 设置世界坐标系的左上角位置

    // 检查内存是否已经初始化（即 Isinitialized 是否为 false）
    if (!GameState->IsInitialized) {
        memory_arena TotalArena;
        // 初始化用于分配全局内存的 Arena（内存池）
        InitializeArena(&TotalArena,
                        // 传递 GameState 中的 WorldArena
                        // Arena 的总大小为永久存储的剩余空间
                        Memory->PermanentStorageSize - sizeof(game_state),
                        // Arena 的基地址为永久存储的起始地址加上 game_state 的大小
                        (uint8 *)Memory->PermanentStorage + sizeof(game_state));
        SubArena(&GameState->AudioArena, &TotalArena, Megabytes(1));
        SubArena(&GameState->ModeArena, &TotalArena, GetArenaSizeRemaining(&TotalArena));
        InitializeAudioState(&GameState->AudioState, &GameState->AudioArena);

        // 标记内存已初始化，避免再次进行初始化操作
        GameState->IsInitialized = true;  // 将 Isinitialized 标记为 true，表示内存已经初始化
    }
    // NOTE: Transient初始化
    Assert(sizeof(transient_state) <= Memory->PermanentStorageSize);
    transient_state *TranState = (transient_state *)Memory->TransientStorage;
    if (!TranState->Initialized) {
        // 初始化 TransientArena，指定其大小和起始地址
        // Memory->TransientStorageSize：可用的瞬时存储空间大小
        // (uint8 *)Memory->TransientStorage：瞬时存储的起始地址，用于管理动态分配
        InitializeArena(&TranState->TransientArena,
                        Memory->TransientStorageSize - sizeof(transient_state),
                        (uint8 *)Memory->TransientStorage + sizeof(transient_state));
        for (uint32 TaskIndex = 0; TaskIndex < ArrayCount(TranState->Tasks); ++TaskIndex) {
            task_with_memory *Task = TranState->Tasks + TaskIndex;
            Task->BeingUsed = false;
            SubArena(&Task->Arena, &TranState->TransientArena, Megabytes(1));
        }

        TranState->Assets = AllocateGameAssets(&TranState->TransientArena, Megabytes(256),
                                               TranState, &Memory->TextureOpQueue);

        // GameState->Music =
        //     PlaySound(&GameState->AudioState, GetFirstSoundFrom(TranState->Assets, Asset_Music));
        // SubArena(&TranState->Assets->AssetArena, &TranState->TransientArena, Megabytes(64));
        TranState->Initialized = true;
        TranState->Assets->TranState = TranState;

        TranState->LowPriorityQueue = Memory->LowPriorityQueue;

        // 创建一个空的位图用于测试，大小为256x256，存储在瞬时内存中
        GameState->TestDiffuse = MakeEmptyBitmap(&TranState->TransientArena, 256, 256, false);

        // 绘制一个矩形填充整个位图，颜色为中性灰（RGB为0.5），不透明（Alpha为1.0）
        // DrawRectangle(&GameState->TestDiffuse, v2{0, 0},
        //               v2i(GameState->TestDiffuse.Width, GameState->TestDiffuse.Height),
        //               v4{0.5f, 0.5f, 0.5f, 1.0f});

        // 创建一个空的位图来存储测试用的法线图（TestNormal），
        // 该位图的宽度和高度与测试的漫反射图（TestDiffuse）相同
        // 使用 TranState 的临时内存池来分配内存，且不进行初始化（false）
        GameState->TestNormal =
            MakeEmptyBitmap(&TranState->TransientArena, GameState->TestDiffuse.Width,
                            GameState->TestDiffuse.Height, false);
#if 1
        // 使用 0.0f 的粗糙度值，生成一个球形法线图并赋值给 TestNormal
        // 该法线图将在后续的渲染或计算中使用
        MakeSphereNormalMap(&GameState->TestNormal, 0.0f);
        MakeSphereDiffuseMap(&GameState->TestDiffuse);
#else
        MakePyramidNormalMap(&GameState->TestNormal, 0.0f);
#endif
        // 环境贴图的宽度
        TranState->EnvMapWidth = 512;

        // 环境贴图的高度
        TranState->EnvMapHeight = 256;

        // 遍历环境映射数组中的每个环境贴图
        for (uint32 MapIndex = 0; MapIndex < ArrayCount(TranState->EnvMaps); MapIndex++) {
            // 获取当前环境贴图
            environment_map *Map = TranState->EnvMaps + MapIndex;
            // 获取环境图的初始宽度和高度
            uint32 Width = TranState->EnvMapWidth;
            uint32 Height = TranState->EnvMapHeight;

            // 遍历每个 LOD (细节层次) 并为每个层级创建空的位图
            for (uint32 LODIndex = 0; LODIndex < ArrayCount(Map->LOD); LODIndex++) {
                // 为当前 LOD 创建一个空的位图，存储到环境图的 LOD 数组中
                Map->LOD[LODIndex] = MakeEmptyBitmap(&TranState->TransientArena,  //
                                                     Width, Height);
                // 每创建一个新的 LOD，宽度和高度都减半
                Width >>= 1;
                Height >>= 1;
            }
        }
        TranState->Initialized = true;
        // PlayIntroCutscene(GameState, TranState);
        if (GameState->GameMode == GameMode_None) {
#if 0
            PlayTitleScreen(GameState, TranState);
#else
            PlayWorld(GameState, TranState);
            game_controller_input *Controller = GetController(Input, 0);
            Controller->Start.EndedDown = true;
            Controller->Start.HalfTransitionCount = 1;
#endif
        }
    }
    {
        DEBUG_DATA_BLOCK("Memory");
        memory_arena *ModeArena = &GameState->ModeArena;
        DEBUG_VALUE(ModeArena);
        memory_arena *AudioArena = &GameState->AudioArena;
        DEBUG_VALUE(AudioArena);
        memory_arena *TransientArena = &TranState->TransientArena;
        DEBUG_VALUE(TransientArena);
    }

    if (TranState->MainGenerationID) {
        EndGeneration(TranState->Assets, TranState->MainGenerationID);
    }
    TranState->MainGenerationID = BeginGeneration(TranState->Assets);
#if 0
    v2 MusicVolume;
    MusicVolume.y = SafeRatio0((real32)Input->MouseX, (real32)Buffer->Width);
    MusicVolume.x = 1.0f - MusicVolume.y;
    ChangeVolume(&GameState->AudioState, GameState->Music, 0.01f, MusicVolume);
    // 将瓦片的像素大小与瓦片的实际尺寸（以米为单位）之间的转换关系存储在MetersToPixels
    // 中。 计算出每米对应的像素数，用于将实际距离（以米为单位）转换为像素距离（以像素为单位）。
    // 公式为：每米像素数 = 瓦片像素边长 / 瓦片实际边长（米）。
#endif

    temporary_memory RenderMemory = BeginTemporaryMemory(&TranState->TransientArena);
    render_group RenderGroup_ =
        BeginRenderGroup(TranState->Assets, RenderCommands, TranState->MainGenerationID, false,
                         RenderCommands->Width, RenderCommands->Height);
    render_group *RenderGroup = &RenderGroup_;

    loaded_bitmap DrawBuffer = {};
    DrawBuffer.Width = (int16)RenderCommands->Width;
    DrawBuffer.Height = (int16)RenderCommands->Height;

    bool32 Return = false;
    do {
        switch (GameState->GameMode) {
            case GameMode_TitleScreen: {
                Return = UpdateAndRenderTitleScreen(GameState, TranState, RenderGroup, &DrawBuffer,
                                                    GameState->TitleScreen, Input);
            } break;
            case GameMode_CutScene: {
                Return = UpdateAndRenderCutscene(GameState, TranState, RenderGroup, &DrawBuffer,
                                                 Input, GameState->Cutscene);
            } break;
            case GameMode_World: {
                Return = UpdateAndGameRenderWorld(GameState, GameState->WorldMode, TranState, Input,
                                                  TranState->Assets, RenderGroup, &DrawBuffer);
            } break;
                InvalidDefaultCase;
        }
    } while (Return);

    EndRender(RenderGroup);

    EndTemporaryMemory(RenderMemory);
#if 0
    if (!HerosExist && QuitRequested) {
        Memory->QuitRequested = true;
    }
#endif
    // 检查世界和过渡区域的内存状态
    CheckArena(&GameState->ModeArena);
    CheckArena(&TranState->TransientArena);
}
